#include "Writer.h"


using namespace std;

Writer::Writer(SystemView* s, string outputName, vector<double>* drt)
{
	sys=s;
	algos.open("../DataBase/OutputFiles/algos.txt");
	priority.open("../DataBase/OutputFiles/LunPriority.txt");
	response3.open("../DataBase/OutputFiles/LunResponseTimeReport3.txt");
	exchange.open("../DataBase/OutputFiles/NumOfExchanges.txt");
	log.open("../DataBase/OutputFiles/logFile.txt");
	LunsRTs.open("../DataBase/OutputFiles/AllLunResponseTimeReport.txt");
	outputFilesName=outputName;
	desiredRT = drt;
}

Writer::~Writer(void)
{
	algos.close();
	priority.close();
	response3.close();
	exchange.close();
	log.close();
	LunsRTs.close();
}

void Writer::setSys(SystemView* s){
	sys=s;
}

void Writer::writeOpt(double hitRatio){
	algos<<"Optimal\n";
	ofstream out;
    out.open("../DataBase/OutputFiles/AlgorithmOptimalReport.txt");
	out<<"hitRatio\n";
	out<<hitRatio;
}

void Writer::writeNext(double hitRatio){
	algos<<"Next\n";
	ofstream out;
	out.open("../DataBase/OutputFiles/AlgorithmNextReport.txt");
	out<<"hitRatio\n";
	out<<hitRatio;
}

void Writer::writeCostAlgo(double hitRatio, int limit){
	algos<<"NextCost"<<limit<<"\n";
	
	ofstream out;
	string fileName="AlgorithmNextCost";
	fileName+=limit;
	fileName+="Report.txt";
	out.open("../DataBase/OutputFiles/" + fileName);
	out<<"hitRatio\n";
	out<<hitRatio<<endl;
	out.close();
}

void Writer::DeviceUtilStatistics(string iteration){
	ofstream out;
    out.open("../DataBase/OutputFiles/DeviceUtilizationReport"+iteration+".txt");
	int i;
	int num_times_slices = sys->getNof_time_slices();
	int time_Slice;
	DeviceData* ssd=sys->getDevice("SSD");
	DeviceData* fc=sys->getDevice("FC");
	DeviceData* sata=sys->getDevice("SATA");
	out<<(num_times_slices-1)<<"\n";
	for (i=1; i<num_times_slices; i++){
		out<<"time:\t";
		time_Slice = sys->getTimeSliceSize();
		out<<(i*time_Slice + time_Slice)<<"\n";
		out<<"SSD"<<"\t";
		out<< ssd->getUtilizationRecord(i*time_Slice + time_Slice)<<"\n";	// utilization
		out<<"FC"<<"\t";
		out<< fc->getUtilizationRecord(i*time_Slice + time_Slice)<<"\n";	// utilization
		out<<"SATA"<<"\t";
		out<< sata->getUtilizationRecord(i*time_Slice + time_Slice)<<"\n";	// utilization
	}
}

void Writer::writeGroupRTNoGraph(string i){
	ofstream out;
	out.open("../DataBase/OutputFiles/LunResponseTimeReport"+i+".txt");
	out<< sys->getMy_luns()->size() <<endl;
	map<double, list<Lun*>*>* lunGroups = sys->getAllocation()->getGroups();
	map<double, list<Lun*>*>::iterator groupIter;
	LunTotalStatistics* totalStats;
	LunTimeStatistics* timeStats;
	map<int,LunTimeStatistics*>::iterator RTiter;
	double avgRT, avgTimeRT, response_time;
	LUNID lid;
	int numLuns, counter = 1, average_count;
	vector <double> averageResponseTimes;
	for (groupIter = lunGroups->begin(); groupIter != lunGroups->end(); groupIter++){
		avgRT = 0;
		average_count = 0;
		out<<"\n";
		out<<"\n";
		out << "Group " << counter << "- Desired Response Time: " << (groupIter->first*1000) << " miliseconds" << endl;
		list<Lun*>::iterator lunIt;
		for (int currentTime=sys->getTimeSliceSize(); currentTime<=(sys->getNof_time_slices()*sys->getTimeSliceSize()); currentTime+=sys->getTimeSliceSize()){
			avgTimeRT = 0;
			numLuns=0;
			for (lunIt = groupIter->second->begin(); lunIt != groupIter->second->end(); lunIt++){
				lid = (*lunIt)->getM_uid();
				totalStats = sys->getLunTotalStatistics(lid);
				timeStats = totalStats->getLunTimeStatistics(currentTime);
				if (timeStats!=0){
					response_time=timeStats->getResponseTime();
					numLuns++;
					avgTimeRT += response_time;
				}
			}
			if (numLuns > 0){
				avgTimeRT=avgTimeRT/numLuns;
			}
			out<<"Time: "<<currentTime<<endl;
			out<<"Group's average response time: "<< avgTimeRT*1000 << endl;
			out<<"\n";
		}
		counter++;
	}
	out.close();
}

// for storage simulation comparison - RT in milliseconds
void Writer::writeAllRts(){
	ofstream allRTs;
	allRTs.open("../DataBase/OutputFiles/AllLunResponseTimeReport.txt");
	LunTotalStatistics* totalStats;
	LunTimeStatistics* timeStats;
	map<int,LunTimeStatistics*>::iterator RTiter;
	double response_time;
	LUNID lid;
	map<double, list<Lun*>*>* lunGroups = sys->getAllocation()->getGroups();
	map<double, list<Lun*>*>::iterator groupIter;
	list<Lun*>::iterator lunIt;
	for (int currentTime=sys->getTimeSliceSize(); currentTime<=(sys->getNof_time_slices()*sys->getTimeSliceSize()); currentTime+=sys->getTimeSliceSize()){
		allRTs << currentTime <<endl;
		for (groupIter = lunGroups->begin(); groupIter != lunGroups->end(); groupIter++){
			for (lunIt = groupIter->second->begin(); lunIt != groupIter->second->end(); lunIt++){
				lid = (*lunIt)->getM_uid();
				totalStats = sys->getLunTotalStatistics(lid);
				timeStats = totalStats->getLunTimeStatistics(currentTime);
				if (timeStats!=0){
					response_time=timeStats->getResponseTime();
					allRTs << lid << "\t" << response_time*1000<<endl;
				}
			}
		}
	}
}

void Writer::writeGroupRT(string iteration){
	if (iteration.compare("3")==0)
		writeAllRts();	// for storage simulation comparison
	ofstream out;
    out.open("../DataBase/OutputFiles/LunResponseTimeReport"+iteration+".txt");
	int i;
	int num_times_slices = sys->getNof_time_slices();
	int time_Slice;
	map<double, list<Lun*>*>* lunGroups = sys->getAllocation()->getGroups();
	map<double, list<Lun*>*>::iterator groupIter;
	for (groupIter = lunGroups->begin(); groupIter != lunGroups->end(); groupIter++){
		out<<"group1 desired response time:\t"<<(groupIter->first*1000) << " miliseconds" << endl;
	}
	out<<(num_times_slices-1)<<endl;
	// starting to write average response times
	map<int, double> group1, group2, group3;
	LunTotalStatistics* totalStats;
	LunTimeStatistics* timeStats;
	map<int,LunTimeStatistics*>::iterator RTiter;
	double avgRT, avgTimeRT, response_time;
	LUNID lid;
	int numLuns, counter = 1, average_count;
	vector <double> averageResponseTimes;
	int groupNum=1;
	for (groupIter = lunGroups->begin(); groupIter != lunGroups->end(); groupIter++){
		avgRT = 0;
		average_count = 0;
		
		list<Lun*>::iterator lunIt;
		for (int currentTime=sys->getTimeSliceSize(); currentTime<=(sys->getNof_time_slices()*sys->getTimeSliceSize()); currentTime+=sys->getTimeSliceSize()){
			avgTimeRT = 0;
			numLuns=0;
			for (lunIt = groupIter->second->begin(); lunIt != groupIter->second->end(); lunIt++){
				lid = (*lunIt)->getM_uid();
				totalStats = sys->getLunTotalStatistics(lid);
				timeStats = totalStats->getLunTimeStatistics(currentTime);
				if (timeStats!=0){
					response_time=timeStats->getResponseTime();
					if (response_time!=0){
						numLuns++;
						avgTimeRT += response_time;
					}
				}
			}
			if (numLuns > 0){
				avgTimeRT=avgTimeRT/numLuns;
			}
			if (groupNum==1)
				group1.insert(pair<int, double>(currentTime, avgTimeRT*1000));
			else if (groupNum==2)
				group2.insert(pair<int, double>(currentTime, avgTimeRT*1000));
			else
				group3.insert(pair<int, double>(currentTime, avgTimeRT*1000));
		}
		groupNum++;
		counter++;
	}
	
	map<int, string>::iterator mapIter;
	for (i=1; i<num_times_slices; i++){
		out<<"time:\t";
		time_Slice = sys->getTimeSliceSize();
		out<<(i*time_Slice + time_Slice)<<"\n";
		out<<"group1:"<<"\t";
		out<< (*group1.find(i*time_Slice + time_Slice)).second<<"\n";
		out<<"group2:"<<"\t";
		out<< (*group2.find(i*time_Slice + time_Slice)).second<<"\n";
		out<<"group3:"<<"\t";
		out<< (*group3.find(i*time_Slice + time_Slice)).second<<"\n";
	}

	out.close();
}

// for storage simulation comparison
void Writer::writeAllRts(int time_Slice){
	LunsRTs << time_Slice <<endl;
	LunTotalStatistics* totalStats;
	LunTimeStatistics* timeStats;
	map<int,LunTimeStatistics*>::iterator RTiter;
	double response_time;
	LUNID lid;
	map<double, list<Lun*>*>* lunGroups = sys->getAllocation()->getGroups();
	map<double, list<Lun*>*>::iterator groupIter;
	list<Lun*>::iterator lunIt;
	for (groupIter = lunGroups->begin(); groupIter != lunGroups->end(); groupIter++){
		for (lunIt = groupIter->second->begin(); lunIt != groupIter->second->end(); lunIt++){
			lid = (*lunIt)->getM_uid();
			totalStats = sys->getLunTotalStatistics(lid);
			timeStats = totalStats->getLunTimeStatistics(time_Slice);
			if (timeStats!=0){
				response_time=timeStats->getResponseTime();
				LunsRTs << lid << "\t" << response_time*1000<<endl;
			}
		}
	}
}

void Writer::writeGroupRT(int time_Slice){
	writeAllRts(time_Slice);
	int num_times_slices = sys->getNof_time_slices();
	map<double, list<Lun*>*>* lunGroups = sys->getAllocation()->getGroups();
	map<double, list<Lun*>*>::iterator groupIter;
	if (time_Slice==3600){
		for (groupIter = lunGroups->begin(); groupIter != lunGroups->end(); groupIter++){
			response3<<"group1 desired response time:\t"<<(groupIter->first*1000) << " miliseconds" << endl;
		}
		response3 << 38 << endl;
		return;
	}
	// starting to write average response times
	map<int, double> group1, group2, group3;
	LunTotalStatistics* totalStats;
	LunTimeStatistics* timeStats;
	map<int,LunTimeStatistics*>::iterator RTiter;
	double avgRT, avgTimeRT, response_time;
	LUNID lid;
	int numLuns, counter = 1, average_count;
	vector <double> averageResponseTimes;
	int groupNum=1;
	for (groupIter = lunGroups->begin(); groupIter != lunGroups->end(); groupIter++){
		avgRT = 0;
		average_count = 0;
		
		list<Lun*>::iterator lunIt;
		avgTimeRT = 0;
		numLuns=0;
		for (lunIt = groupIter->second->begin(); lunIt != groupIter->second->end(); lunIt++){
			lid = (*lunIt)->getM_uid();
			totalStats = sys->getLunTotalStatistics(lid);
			timeStats = totalStats->getLunTimeStatistics(time_Slice);
			if (timeStats!=0){
				response_time=timeStats->getResponseTime();
				if (response_time!=0){
					numLuns++;
					avgTimeRT += response_time;
				}
			}
		}
		if (numLuns > 0){
			avgTimeRT=avgTimeRT/numLuns;
		}
		if (groupNum==1)
			group1.insert(pair<int, double>(time_Slice, avgTimeRT*1000));
		else if (groupNum==2)
			group2.insert(pair<int, double>(time_Slice, avgTimeRT*1000));
		else
			group3.insert(pair<int, double>(time_Slice, avgTimeRT*1000));
		groupNum++;
		counter++;
	}
	
	map<int, string>::iterator mapIter;
	response3<<"time:\t";
	response3<<(time_Slice)<<"\n";
	response3<<"group1:"<<"\t";
	response3<< (*group1.find(time_Slice)).second<<"\n";
	response3<<"group2:"<<"\t";
	response3<< (*group2.find(time_Slice)).second<<"\n";
	response3<<"group3:"<<"\t";
	response3<< (*group3.find(time_Slice)).second<<"\n";
}

void Writer::writeGroupResponseTimes(string i){
	ofstream out;
	out.open("../DataBase/OutputFiles/LunResponseTimeReport"+i+".txt");
	out<< sys->getMy_luns()->size() <<endl;
	map<double, list<Lun*>*>* lunGroups = sys->getAllocation()->getGroups();
	map<double, list<Lun*>*>::iterator groupIter;
	LunTotalStatistics* totalStats;
	map<int,LunTimeStatistics*>* responseTimes;
	map<int,LunTimeStatistics*>::iterator RTiter;
	double avgRT, avgTimeRT, response_time;
	LUNID lid;
	int numTimeSlices, counter = 1, average_count;
	vector <double> averageResponseTimes;
	for (groupIter = lunGroups->begin(); groupIter != lunGroups->end(); groupIter++){
		avgRT = 0;
		average_count = 0;
		list<Lun*>::iterator lunIt;
		for (lunIt = groupIter->second->begin(); lunIt != groupIter->second->end(); lunIt++){
			lid = (*lunIt)->getM_uid();
			totalStats = sys->getLunTotalStatistics(lid);
			responseTimes = totalStats->getAllLunTimeStatistics();
			RTiter=responseTimes->begin();
			avgTimeRT = 0;
			numTimeSlices = 0;
			while(RTiter!=responseTimes->end()){
				response_time = (*RTiter).second->getResponseTime();
				if (response_time != 0){
					numTimeSlices++;
					avgTimeRT += response_time;
				}
				RTiter++;
			}
			if (numTimeSlices > 0){
				avgTimeRT=avgTimeRT/numTimeSlices;
				avgRT+=avgTimeRT;
				average_count++;
			}
		}
		avgRT = avgRT/average_count;
		averageResponseTimes.push_back(avgRT);
		counter++;
	}
	out << "" << endl;
	for (int i = 0; i < averageResponseTimes.size(); i++){
		out << "Group " << (i+1) << " Average Response Time: " << (averageResponseTimes.at(i)*1000) << " milliseconds" << endl;
	}
	out.close();
}

void Writer::writeResponseTimes(){
	ofstream out;
	out.open("../DataBase/OutputFiles/LunResponseTimeReport.txt");
	out<< sys->getMy_luns()->size() <<endl;
	map<LUNID, LunTotalStatistics*>* luns = sys->getMy_luns();
	map<LUNID, LunTotalStatistics*>::iterator lunIter;
	LunTotalStatistics* totalStats;
	map<int,LunTimeStatistics*>* responseTimes;
	map<int,LunTimeStatistics*>::iterator RTiter;
	double avgRT=0;
	LUNID lid;
	int numTimeSlices = sys->getNof_time_slices();
	lunIter=luns->begin();
	while(lunIter!=luns->end()){
		lid=(*lunIter).first;
		totalStats = sys->getLunTotalStatistics(lid);
		responseTimes = totalStats->getAllLunTimeStatistics();
		RTiter=responseTimes->begin();
		while(RTiter!=responseTimes->end()){
			avgRT+=(*RTiter).second->getResponseTime();
			RTiter++;
		}
		avgRT=avgRT/numTimeSlices;
		out<<"lun\t"<<lid<<"\t"<<avgRT<<endl;
		lunIter++;
	}

	out.close();
}

void Writer::writeAlgoDamage(int iter, map<int, double>* damage, string algoName, string methodName){
	int numTimeSlices = damage->size();
	if (iter==1){
		algoDamage.open("../DataBase/OutputFiles/"+algoName+"TotalDamageReport.txt");
		algoDamage<<numTimeSlices<<endl;
	}
	algoDamage<<methodName<<endl;
	map<int, double>::iterator diter;
	diter=damage->begin();
	while(diter!=damage->end()){
		algoDamage<<(*diter).first<<"\t"<<(*diter).second<<endl;
		diter++;
	}
	if (iter==4)
		algoDamage.close();
}
	
void Writer::writeMethodDamage(int iter, map<int, double>* damage, string algoName, string methodName){
	int numTimeSlices = damage->size();
	if (iter==1){
		methodDamage.open("../DataBase/OutputFiles/"+methodName+"TotalDamageReport.txt");
		methodDamage<<numTimeSlices<<endl;
	}
	methodDamage<<algoName<<endl;
	map<int, double>::iterator diter;
	diter=damage->begin();
	while(diter!=damage->end()){
		methodDamage<<(*diter).first<<"\t"<<(*diter).second<<endl;
		diter++;
	}
	if (iter==4)
		methodDamage.close();
}

void Writer::writeDivisionOfPriorityGroups(int iteration, int groupNo, map<int,double>* ssd, map<int,double>* fc, map<int,double>* sata){	
	ofstream out;
	char buffer[30];
	_itoa_s(iteration, buffer,30, 10);
	int num_times_slices = sys->getNof_time_slices();
	int time_Slice;
	switch (groupNo){
			case 1:
				out.open("../DataBase/OutputFiles/DivisionFirstGroupReport"+(string)buffer+".txt");
				break;
			case 2:
				out.open("../DataBase/OutputFiles/DivisionSecondGroupReport"+(string)buffer+".txt");
				break;
			case 3:
				out.open("../DataBase/OutputFiles/DivisionThirdGroupReport"+(string)buffer+".txt");
				break;
			default:
				cout<< "priority group not in range [1..3] !!!!  "<< groupNo<< endl;
	}
	out<<num_times_slices<<"\n";
	for (int i=0; i<num_times_slices; i++){
		out<<"time:\t";
		time_Slice = sys->getTimeSliceSize();
		out<<(i*time_Slice + time_Slice)<<"\n";
		out<<"SSD"<<"\t";
		out<< ssd->at(i*time_Slice + time_Slice)<<"\n";
		out<<"FC"<<"\t";
		out<< fc->at(i*time_Slice + time_Slice)<<"\n";
		out<<"SATA"<<"\t";
		out<< sata->at(i*time_Slice + time_Slice)<<"\n";
	}
}

void Writer::writeAllLunPriority(int time){
	double average;
	int count;

	priority << time << endl;
	map<double, list<Lun*>*>::iterator group_iter = sys->getAllocation()->getSystemGroups()->begin();
	list<Lun*>::iterator lun_iter;
	while (group_iter != sys->getAllocation()->getSystemGroups()->end()){
		lun_iter = group_iter->second->begin();
		if (lun_iter!=group_iter->second->end()){
			priority << "Group " << (*lun_iter)->getPriorityGroup() << endl;
			average = 0;
			count = 0;
			while (lun_iter != group_iter->second->end()){
				priority << (*lun_iter)->getM_uid() << "\t" << (*lun_iter)->getPriority() << endl;
				average += (*lun_iter)->getPriority();
				count++;
				lun_iter++;

			}
			if (count > 0){
				average = (average/count);
			}
			else {
				average = 0.0;
			}
		}
		group_iter++;
	}
	priority << "\n";
}

void Writer::writeNumOfExchanges(int time, int num){
	exchange<<time<<": "<<num<<" exchanges"<<endl;
}

void Writer::writeLog(string line){
	if (!log.is_open()){
		log.open("../DataBase/OutputFiles/logFile.txt", ofstream::app);
	}
	log<<line<<endl;
	log.close();
}

ofstream* Writer::logFile(){
	return &log;
}

// lunID	priorityGroup
void Writer::writePriorityGroups(){
	ofstream groups;
	string outputFile = "E:/InputFiles/LunsPriorityGroups/Groups_" + outputFilesName;
	groups.open(outputFile, ios::app);
	groups<< desiredRT->at(0) << "\t" << desiredRT->at(1) << "\t" << desiredRT->at(2) <<endl;
	map<LUNID, LunTotalStatistics*>* luns = sys->getMy_luns();
	map<LUNID, LunTotalStatistics*>::iterator it = luns->begin();
	while(it!=luns->end()){
		groups << it->second->getLun()->getM_uid()<<"\t"<<it->second->getLun()->getPriorityGroup()<<endl;
		it++;
	}
	groups.close();
}